package com.sz.biz.app.web.security;

import com.sz.common.base.constants.UnitSetting;
import com.sz.common.base.exception.BizException;
import com.sz.common.base.utils.SerializeUtil;
import com.sz.common.core.service.Principal;
import com.sz.common.core.service.PrincipalManager;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.UnavailableSecurityManagerException;
import org.apache.shiro.session.Session;
import org.springframework.core.serializer.DefaultDeserializer;
import org.springframework.core.serializer.support.DeserializingConverter;

import java.util.Map;

/**
 * Function: AbstractPrincipalManager <br>
 * Author: Charles <br>
 * Date: 2017-01-20 13:34:00
 */
public abstract class AbstractPrincipalManager implements PrincipalManager {
    protected static final String PRINCIPAL_KEY = "global.principal";

    @Override
    public Principal getPrincipal() {
        try {
            Session session = SecurityUtils.getSubject().getSession();
            Principal principal = getPrincipal(session);
            if (principal != null) {
                principal.setListener(this);
            }
            return principal;
        } catch (UnavailableSecurityManagerException ex) {
            if (UnitSetting.isUnitTest()) { //ONLY FOR UNIT TEST
                return (Principal) UnitSetting.getMockObject("principal");
            }
            throw ex;
        }
    }

    @Override
    public void principalUpdated(Principal principal) {
        Session session = SecurityUtils.getSubject().getSession();
        session.setAttribute(PRINCIPAL_KEY, principal);
    }

    /**
     * @param session
     * @return
     */
    @Override
    public Principal getPrincipal(Session session) {
        if (session == null) {
            return null;
        }
        try {
            Principal principal = (Principal) session.getAttribute(PRINCIPAL_KEY);
            if (principal == null && isAnonymousSupported()) {
                principal = createAnonymousPrincipal();
                session.setAttribute(PRINCIPAL_KEY, principal);
            }
            return principal;
        } catch (ClassCastException ex) {
            //springboot-devtools
            try {
                Object obj = session.getAttribute(PRINCIPAL_KEY);
                ClassLoader cl = Principal.class.getClassLoader();
                DeserializingConverter dz = new DeserializingConverter(new DefaultDeserializer(cl));
                return (Principal) dz.convert(SerializeUtil.serialize(obj));
            } catch (Exception e) {
                return null;
            }
        } catch (Exception e) {
            throw new BizException(500, e.getMessage());
        }
    }

    @Override
    public Principal createAnonymousPrincipal() {
        return null;
    }

    /**
     * @param appDomain   --域Id
     * @param accountId   --账户Id
     * @param accountName --账户名称
     * @param props       --其它属性
     * @return
     */
    @Override
    public Principal setupPrincipal(int appDomain, int accountId, String accountName, Map<String, Object> props) {
        Principal principal = createPrincipal(appDomain, accountId, accountName, props);
        SecurityUtils.getSubject().getSession().setAttribute(PRINCIPAL_KEY, principal);
        return principal;
    }

    @Override
    public boolean isAnonymousSupported() {
        return false;
    }

    /**
     * @param appDomain
     * @param accountId
     * @param accountName
     * @param props
     * @return
     */
    protected abstract Principal createPrincipal(int appDomain, int accountId, String accountName, Map<String, Object> props);

}
